package cn.stylefeng.guns.modular.launchpad.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.guns.core.common.page.LayuiPageFactory;
import cn.stylefeng.guns.core.exceptions.BusinessException;
import cn.stylefeng.guns.modular.app.controller.market.Kline;
import cn.stylefeng.guns.modular.base.state.Constant;
import cn.stylefeng.guns.modular.base.state.F;
import cn.stylefeng.guns.modular.base.util.RedisUtil;
import cn.stylefeng.guns.modular.coin.entity.Spot;
import cn.stylefeng.guns.modular.coin.service.SpotService;
import cn.stylefeng.guns.modular.launchpad.entity.Distribution;
import cn.stylefeng.guns.modular.launchpad.entity.Pledge;
import cn.stylefeng.guns.modular.launchpad.entity.Product;
import cn.stylefeng.guns.modular.launchpad.mapper.ProductMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ProductService extends ServiceImpl<ProductMapper, Product> {

    @Autowired
    private PledgeService pledgeService;
    @Autowired
    private DistributionService distributionService;


    @Autowired
    private RedisUtil redisUtil;
    private static final Logger logger = LoggerFactory.getLogger(ProductService.class);

    @Autowired
    private SpotService spotService;

    /**
     * 查询列表
     */
    public IPage<Product> selectByCondition(Product project) {
        Page<Product> pageParam = LayuiPageFactory.defaultPage();
        LambdaQueryWrapper<Product> qw = new LambdaQueryWrapper<>();
        if (StrUtil.isNotEmpty(project.getName())) {
            qw.like(Product::getName, project.getName());
        }
        qw.orderByDesc(Product::getIsHot);
        qw.orderByDesc(Product::getSortNum);
        IPage<Product> page = this.page(pageParam, qw);
        return page;
    }


    /**
     * 添加|修改
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveProduct(Product project) {
        if (StrUtil.isEmpty(project.getName())) {
            throw new BusinessException("分红币种不能为空");
        }
        if (StrUtil.isEmpty(project.getPledgeCoins())) {
            throw new BusinessException("质押币种不能为空");
        }
        if (StrUtil.isEmpty(project.getTotalReward())) {
            throw new BusinessException("每次分红总金额不能为空");
        }
        if (project.getCycleDay() == null) {
            throw new BusinessException("挖矿周期不能为空");
        }
        if (project.getStartTime() == null) {
            throw new BusinessException("开始时间不能为空");
        }
        if (project.getIntervalTime() == null) {
            throw new BusinessException("分红间隔时间不能为空");
        }

        // 计算结束时间 = 开始时间 + 挖矿周期
        DateTime endTime = DateUtil.offsetDay(project.getStartTime(), project.getCycleDay());
        project.setEndTime(endTime);

        if (project.getId() == null) {
            project.setEnableFlag(0);
            project.setCreateTime(new Date());
            this.baseMapper.insert(project);
        } else {
            Product oldProject = this.getById(project.getId());
            if (oldProject == null) {
                throw new BusinessException("项目不存在");
            }
            if (!StrUtil.equals(oldProject.getPledgeCoins(), project.getPledgeCoins())) {
                List<String> oldPledgeCoinList = StrUtil.split(oldProject.getPledgeCoins(), ',');
                List<String> newPledgeCoinList = StrUtil.split(project.getPledgeCoins(), ',');

                // 如果旧的质押币种不存在于新的质押币种中, 则校验是否有质押记录
                for (String oldPledgeCoin : oldPledgeCoinList) {
                    if (!newPledgeCoinList.contains(oldPledgeCoin)) {
                        // int count = pledgeService.count(new LambdaQueryWrapper<Pledge>()
                        //         .eq(Pledge::getProductId, project.getId())
                        //         .eq(Pledge::getPledgeCoin, oldPledgeCoin));
                        // if (count > 0) {
                        //     throw new BusinessException("该项目有质押记录,不能删除质押币种" + oldPledgeCoin);
                        // }

                        List<Pledge> coinPledgeList = pledgeService.list(new LambdaQueryWrapper<Pledge>()
                                .eq(Pledge::getProductId, project.getId())
                                .eq(Pledge::getPledgeCoin, oldPledgeCoin));
                        // 如果统计质押数量为0 可以删除
                        BigDecimal coinTotalPledgeNum = coinPledgeList.stream()
                                .map(Pledge::getPledgeNum).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);
                        if (!coinTotalPledgeNum.equals(BigDecimal.ZERO)) {
                            throw new BusinessException("该项目有质押记录,不能删除质押币种" + oldPledgeCoin);
                        }

                    }
                }
            }


            project.setUpdateTime(new Date());
            this.baseMapper.updateById(project);
        }
    }

    /**
     * 删除新币挖矿
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteProduct(Long id) {
        List<Pledge> pledgeList = pledgeService.getByProductId(id);
        if (pledgeList.size() > 0) {
            throw new BusinessException("该项目有质押记录,不能删除");
        }

        this.baseMapper.deleteById(id);
    }

    public List<Product> getListForApp(Product param) {
        LambdaQueryWrapper<Product> qw = new LambdaQueryWrapper<>();
        // qw.eq(Product::getEnableFlag, 1);
        qw.orderByDesc(Product::getIsHot);
        qw.orderByDesc(Product::getSortNum);
        List<Product> projectList = this.baseMapper.selectList(qw);

        List<Spot> spots = F.me().getSpots(null);


        // 获取BNB价格
        BigDecimal bnbPrice = getCoinPrice("BNB");

        Date now = new Date();

        for (Product product : projectList) {
            // 获取质押记录
            List<Pledge> pledgeList = pledgeService.getByProductId(product.getId());
            // 统计参与人数
            long participantNum = pledgeList.stream().map(Pledge::getMemberId).distinct().count();
            product.setParticipantNum((int) participantNum);

            // 统计发行数量
            List<Distribution> distributionList = distributionService.list(new LambdaUpdateWrapper<Distribution>()
                    .eq(Distribution::getProductId, product.getId()));

            BigDecimal totalDistribution = distributionList.stream()
                    .map(Distribution::getDistributionNum).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);

            product.setTotalDistribution(totalDistribution.toString());

            // 分发币种等价于多少BNB
            BigDecimal coinPrice = getCoinPrice(product.getName());
            BigDecimal equivalenceBnbPrice = convertCoinPriceToBnB(coinPrice, bnbPrice);
            product.setEquivalenceBnbPrice(equivalenceBnbPrice.toString());


            // 计算总投入量 等价于多少BNB
            BigDecimal totalInvestmentPrice = BigDecimal.ZERO;

            // 小计奖池
            List<Pledge> poolList = new ArrayList<>();
            // 按质押币种分组
            Map<String, List<Pledge>> groupMapList = pledgeList.stream().collect(Collectors.groupingBy(Pledge::getPledgeCoin));
            // 按getPledgeCoins 对groupMapList进行排序
            List<String> splitList = StrUtil.split(product.getPledgeCoins(), ',');
            groupMapList = groupMapList.entrySet().stream()
                    .sorted((o1, o2) -> {
                        int index1 = splitList.indexOf(o1.getKey());
                        int index2 = splitList.indexOf(o2.getKey());
                        return index1 - index2;
                    })
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));



            for (Map.Entry<String, List<Pledge>> entry : groupMapList.entrySet()) {
                String pledgeCoin = entry.getKey();
                List<Pledge> itemList = entry.getValue();
                BigDecimal subTotalPledgeNum = itemList.stream()
                        .map(Pledge::getPledgeNum).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);

                int memberCount = itemList.size();


                Pledge pledge = new Pledge();
                pledge.setPledgeCoin(pledgeCoin);
                pledge.setPledgeNum(subTotalPledgeNum.toString()); // 单奖池存入总量
                pledge.setMemberCount(memberCount); // 单奖池参与人数

                // 累计挖矿奖励总量 = 这个币种的所有质押记录的挖矿奖励总量
                BigDecimal subTotalDistributionNum = distributionList.stream()
                        .filter(distribution -> distribution.getPledgeCoin().equals(pledgeCoin))
                        .map(Distribution::getDistributionNum).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);
                pledge.setSubTotalDistributionNum(subTotalDistributionNum.toString()); //  累计挖矿奖励总量

                BigDecimal todayDistributionNum = distributionList.stream()
                        .filter(distribution -> distribution.getPledgeCoin().equals(pledgeCoin))
                        .filter(distribution -> DateUtil.isSameDay(distribution.getCreateTime(), new Date()))
                        .map(Distribution::getDistributionNum).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);

                pledge.setTodayDistributionNum(todayDistributionNum.toString()); //  今日分红总量

                for (Spot spot : spots) {
                    if (spot.getSymbol().equals(pledgeCoin + "/USDT")) {
                        pledge.setLogoUrl(spot.getImg());
                        break;
                    }
                }

                poolList.add(pledge);


                BigDecimal equivalenceBnbCount = BigDecimal.ZERO;
                if (pledgeCoin.equals("BNB")) {
                    // 累加
                    equivalenceBnbCount = itemList.stream()
                            .map(Pledge::getPledgeNum).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);
                } else {
                    // 等价与多少BNB
                    BigDecimal pledgeCoinPrice = getCoinPrice(pledgeCoin);
                    BigDecimal pledgeCoinEquivalenceBnbPrice = convertCoinPriceToBnB(pledgeCoinPrice, bnbPrice);

                    for (Pledge item : itemList) {
                        equivalenceBnbCount = equivalenceBnbCount.add(pledgeCoinEquivalenceBnbPrice.multiply(new BigDecimal(item.getPledgeNum())));
                    }
                }

                totalInvestmentPrice = totalInvestmentPrice.add(equivalenceBnbCount);

            }
            product.setTotalInvestmentPrice(totalInvestmentPrice.toString());



            // 当前时间 大于 结束时间, 则设置为已结束
            if (now.after(product.getEndTime())) {
                product.setIsEnd(1);
            } else {
                product.setIsEnd(0);
            }

            // 如果奖池为空, 则设置空奖池
            for (String coinName : splitList) {
                // 如果奖池没有这个币种, 则设置空奖池
                boolean isExist = poolList.stream().anyMatch(e -> e.getPledgeCoin().equals(coinName));
                if (isExist) {
                    continue;
                }
                Pledge pledge = new Pledge();
                pledge.setPledgeCoin(coinName);
                pledge.setPledgeNum("0"); // 单奖池存入总量
                pledge.setMemberCount(0);
                pledge.setSubTotalDistributionNum("0"); //  累计挖矿奖励总量
                pledge.setTodayDistributionNum("0"); //  今日分红总量
                for (Spot spot : spots) {
                    if (spot.getSymbol().equals(coinName + "/USDT")) {
                        pledge.setLogoUrl(spot.getImg());
                        break;
                    }
                }
                poolList.add(pledge);
            }

            product.setPoolList(poolList);

        }


        return projectList;
    }

    private BigDecimal getCoinPrice(String name) {
        String klineType = name + "/USDT";
        Kline entity = (Kline) redisUtil.get(Constant.KINE + klineType + Constant._NEW);
        if (entity == null) {
            throw new BusinessException("获取" + name + "价格失败");
        }
        return BigDecimal.valueOf(entity.getClose());
    }

    /**
     * 将币种价格转换为BNB价格
     *
     * @param coinPrice 币种价格
     * @param BNBPrice  BNB价格
     * @return 等于多少BNB
     */
    private BigDecimal convertCoinPriceToBnB(BigDecimal coinPrice, BigDecimal BNBPrice) {
        return coinPrice.divide(BNBPrice, 8, RoundingMode.HALF_DOWN);
    }


    public Product detail(Long id) {
        return this.getById(id);
    }

    public List<Map<String, Object>> coinList(String productId, String type) {
        List<Spot> spotList = spotService.list(new LambdaUpdateWrapper<Spot>().eq(Spot::getDel, 'N')
                .orderByDesc(Spot::getCreateTime));

        List<Map<String, Object>> dataList = new ArrayList<>();

        String name = null;
        List<String> pledgeCoinList = null;
        if (StrUtil.isNotEmpty(productId) && StrUtil.isNotEmpty(type)) {
            Product product = this.getById(productId);
            name = product.getName();
            pledgeCoinList = StrUtil.split(product.getPledgeCoins(), ',');
        }

        if (CollUtil.isNotEmpty(spotList)) {
            for (Spot spot : spotList) {
                Map<String, Object> map = CollUtil.newHashMap();
                String[] split = spot.getSymbol().split("/");
                if (split.length != 2) {
                    continue;
                }
                String coin = split[0];

                map.put("name", coin);
                map.put("value", coin);

                if ("name".equals(type)) {
                    if (StrUtil.isNotEmpty(name) && StrUtil.equals(name, coin)) {
                        map.put("selected", "selected");
                    }
                } else if ("pledgeCoins".equals(type)) {
                    if (pledgeCoinList != null && pledgeCoinList.contains(coin)) {
                        map.put("selected", "selected");
                    }
                }
                dataList.add(map);
            }
        }

        return dataList;
    }

    public void changeEnableFlag(String type, Long id) {
        Product old = this.getById(id);
        if (old == null) {
            throw new BusinessException("产品不存在");
        }
        Product product = new Product();
        product.setId(id);
        product.setEnableFlag("Y".equals(type) ? 1 : 0);
        this.baseMapper.updateById(product);
    }

    public void changeIsHot(String type, Long id) {
        Product old = this.getById(id);
        if (old == null) {
            throw new BusinessException("产品不存在");
        }
        Product product = new Product();
        product.setId(id);
        product.setIsHot("Y".equals(type) ? 1 : 0);
        this.baseMapper.updateById(product);
    }
}
